package com.opengate.base.redis;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @author 曳戈泰尔
 * @version 1.0
 * @description Redis分布式锁
 * @date 2023/4/26 5:46 PM
 */
@Slf4j
@Component
public class RedisLockHelper {

    /** 注入StringRedisTemplate */
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 根据lockKey加分布式锁，锁值：开始时间 + 超时时间 + 1毫秒
     *
     * @param lockKey 锁的key
     * @param startTime 开始时间
     * @param lockTime 超时时间
     * @return 是否加锁成功
     */
    public boolean getLock(String lockKey, long startTime, long lockTime) {

        // 是否加锁成功
        boolean lock = false;
        // 锁值
        String value = Long.toString(startTime + lockTime + 1);

        try {
            // 尝试加锁
            Boolean isSetNx = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value);
            lock = Objects.nonNull(isSetNx) && isSetNx;

            // 如果加锁失败，判断锁是否超时
            if (!lock) {
                String vaStr = stringRedisTemplate.opsForValue().get(lockKey);
                // 如果锁超时，重新设定锁
                if (StringUtils.isNotEmpty(vaStr)) {
                    long oldValue = Long.parseLong(vaStr);
                    // 如果锁超时，重新设定锁
                    if (oldValue < System.currentTimeMillis()) {
                        log.info("{}，Redis分布式锁已经超时，重新设定锁。", lockKey);

                        // 重新设定锁
                        String getValue =
                                stringRedisTemplate.opsForValue().getAndSet(lockKey, value);

                        // 是否抢占到锁，并设定成功
                        if (StringUtils.isNotEmpty(getValue)
                                && (Long.parseLong(getValue) == oldValue)) {
                            // 抢占到锁
                            lock = true;
                        }
                    }
                }
            }

            log.info("{}，获得Redis分布式锁 {}", lockKey, lock);
        } catch (Exception e) {
            log.error("{}，Redis分布式锁锁定异常", lockKey, e);
        }

        return lock;
    }

    /**
     * 根据key删除分布式锁,锁值:开始时间+超时时间+1毫秒
     *
     * @param lockKey 锁的key
     * @param startTime 开始时间
     * @param lockTime 超时时间
     * @return 是否解锁成功
     */
    public boolean delLock(String lockKey, long startTime, long lockTime) {

        // 是否解锁成功
        boolean unLock = false;
        // 锁值
        long value = startTime + lockTime + 1;

        try {
            // 获取锁值
            String strVa = stringRedisTemplate.opsForValue().get(lockKey);
            long oldValue = StringUtils.isEmpty(strVa) ? 0 : Long.parseLong(strVa);

            // 如果锁值相等，删除锁
            if (oldValue == value) {
                Boolean isDel = stringRedisTemplate.delete(lockKey);
                unLock = Objects.nonNull(isDel) && isDel;
            }

            log.info("{}，Redis分布式锁解锁 {}.", lockKey, unLock);
        } catch (Exception e) {
            log.error("{}，Redis分布式锁解锁异常", lockKey, e);
        }

        return unLock;
    }
}
